﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Netfraction.Network.Protocol.BASE.Handler
{
    public class HandlerGET : ProtocolMessageActionHandler
    {
        public HandlerGET()
            : base()
        {
            mSupportedConnectionStates = new List<RemoteMachine.ConnectionState>();
        }

        #region ProtocolMessageActionHandler Members

        public override string ProtocolActionCode
        {
            get { return "GET"; }
        }

        // this handler is way to long. break it into smaller functions
        public override bool HandleProtocolMessage(Netfraction.RemoteMachine.Node node, ProtocolMessage message, IEnumerable<ProtocolFeature> extenders)
        {
            if (node is RemoteMachine.Peer) // The Hub is in this Peer list I believe. 
                                            // Since extensions can enable hub-client array transfer, perhaps something should cater for that here?
            {
                System.Diagnostics.Debug.WriteLine("in GET handler");
                if (message.Parameters.Count >= 4)  // allow extension parameters. but must have at least 4.
                {
                    System.Diagnostics.Debug.WriteLine(String.Format("Source SID: {0}", message.SessionId));
                    System.Diagnostics.Debug.WriteLine(String.Format("Type: {0}, Identifier: {1}, Start Position: {2}, Bytes: {3}", 
                        message.Parameters[0].ParameterValue,
                        message.Parameters[1].ParameterValue,
                        message.Parameters[2].ParameterValue,
                        message.Parameters[3].ParameterValue));

                    // all cases will send some variety of Stream, be it a FileStream or MemoryStream

                    string type = message.Parameters[0].ParameterValue;
                    // "Extensions may add to the identifier names as well as add new types."
                    // the TIGR feature adds support for "tthl" too
                    string identifier = message.Parameters[1].ParameterValue;

                    System.IO.MemoryStream mStream = null;
                    Factory.FactorySND.SNDTypesEnum sendType = Factory.FactorySND.SNDTypesEnum.File;    // this is the most common case, so set it as default

                    switch (type)
                    {
                        case "file":
                            // message.GETParameterOptions[1].ParameterValue identifies the file requested
                            // when TIGR supported idenifier is of the form "TTH/<base32-encoded tree root>"
                            // otherwise, it is the Virtual Path to the file

                            // 'files.xml' is BASE feature file list. 'files.xml.bz2' is BZIP feature file list. 
                            // extensions can add other files like this, with 'rootless' filename
                            // All shared files in BASE are relative to the 'unnamed root' i.e. '/'.
                            // Extensions may add 'named roots' e.g. the TIGR feature adds 'TTH/'.
                            // The identifier must come from the namespace e.g. 'TTH/' of the current session hash

                            if (identifier.Contains('/'))
                            {
                                if (identifier[0] == '/')
                                {
                                    // file is somewhere below the named root
                                    int i = identifier.IndexOf('/', 1);
                                    if (i == identifier.Length - 1 || i == 1)
                                    {
                                        // identifier ends in a '/' or starts with '//'. Invalid paths, so fail. Send File_Not_Available STA
                                    }
                                }
                                else
                                {
                                    object o = CallMessageExtensions(node, message, extenders, (int)Extensibility.GETExtensionType.NamedRoot, identifier);
                                    if (o != null && o is string)
                                    {
                                        System.Diagnostics.Debug.WriteLine(o);
                                        identifier = o as string;
                                    }
                                    else
                                    {
                                        identifier = message.Parameters[1].ParameterValue;
                                    }
                                }
                            }
                            else
                            {
                                ShareManager.ShareManager sManager = ShareManager.ShareManager.GetInstance();

                                if (sManager.FileListNames.Contains(identifier))    // check if a registered file list handler can deal with it
                                {
                                    mStream = sManager.CreateFileListStream(identifier);
                                }
                                else
                                {
                                    // it's some other rootless name, see if the extensions want to handle it
                                    /*object featureActions = CallMessageExtensions(node, message, extenders, (int)Extensibility.GETExtensionType.RootlessName, identifier);
                                    if (featureActions != null && featureActions is string)
                                    {
                                        System.Diagnostics.Debug.WriteLine(featureActions);
                                        identifier = featureActions as string;
                                    }
                                    else
                                    {
                                        // okay so our extensions dont handle the identifier. fail! Send File_Not_Available STA
                                    }*/
                                }

                            }

                            // thus, if the name parameter contains no '/' character, it is a file list
                            // if the '/' character is not the first, it is a named root, handled by an extension feature
                            // if the '/' character is the first, the BASE feature should handle the entire request, 
                            // unless an extension has defined additional flags, which may still effect the DATA transfer
                            // e.g. ZLIG adds the 'ZL1' flag to show the datastream should be compressed

                            // GET extensions can be any combination of the following types:
                            // TYPE extensions, ROOTLESS_NAME extensions, NAMED_ROOT extensions, STREAM extensions
                            // TIGR adds the 'tthl' TYPE extension
                            break;
                        case "list": // partial file lists
                            sendType = Factory.FactorySND.SNDTypesEnum.List;
                            break;
                        default: // unrecognised or extension-implemented types
                            object o2 = CallMessageExtensions(node, message, extenders, (int)Extensibility.GETExtensionType.Type);
                            if (o2 is string)
                            {
                                System.Diagnostics.Debug.WriteLine(o2);
                                // extension should deal with interpreting the identifier, and produce a MemoryStream ready to be transmitted
                            }
                            else
                            {
                                // unsupported Type field in GET Handler
                            }
                            break;
                    }

                    // send file, also deal with STREAM extensions e.g. ZLIG and its ZL1 flag

                    if (mStream != null)
                    {
                        long bytesToSend = mStream.Length;

                        ProtocolMessageFactory factory = new Factory.FactorySND();
                        Dictionary<int, object> factoryParams = new Dictionary<int, object>();
                        factoryParams.Add((int)Factory.FactorySND.ParameterEnum.Type, sendType);
                        factoryParams.Add((int)Factory.FactorySND.ParameterEnum.Identifier, identifier);
                        factoryParams.Add((int)Factory.FactorySND.ParameterEnum.StartPosition, "0");
                        factoryParams.Add((int)Factory.FactorySND.ParameterEnum.BytesToSend, bytesToSend.ToString());
                        node.Connection.Send(factory.Create(ProtocolMessage.MessageTypeEnum.Client, factoryParams).ToString());

                        // we probably should pass the stream off to the UploadManager at this point....
                        // Create and dispose of the MemoryStream outside the switch handler
                        node.Connection.State = RemoteMachine.ConnectionState.Data;
                        byte[] buffer = new byte[bytesToSend < 16384 ? bytesToSend : 16384];
                        long bytesSent = 0;

                        while (bytesSent < bytesToSend)
                        {
                            bytesSent += mStream.Read(buffer, 0, buffer.Length);
                            node.Connection.Send(buffer);
                        }
                        node.Connection.State = RemoteMachine.ConnectionState.Normal;
                    }
                }
                return true;
            }
            else
            {
                return false;
            }
        }

        #endregion
    }
}
